/*********************************************************************************
 *      Copyright:  (C) 2025 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  socket_client.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(28/03/25)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "28/03/25 16:30:15"
 *                 
 ********************************************************************************/

#include "socket_client.h"

//socket结构体里的IP，端口初始化
int socket_init(socket_cli *sock, char *host, int port)
{
	if(!sock || port<=0)
		return -1;

	memset( sock, 0, sizeof(*sock) );
	sock->sockfd = -1;
	sock->port = port;
	if( host )
	{
		strncpy(sock->host, host, 64);
	}
	
	return 0;
}


//关闭sockfd
int socket_term(socket_cli *sock)
{
	if( !sock )
		return -1;

	if( sock->sockfd > 0)
	{
		close(sock->sockfd);
		sock->sockfd = -1;
	}

	return 0;
}


//客户端连接
int socket_connect(socket_cli *sock)
{
	int                  rv = -1;
	struct sockaddr_in   servaddr;
	int                  on=1;
	char                 service[20];
	struct addrinfo      hints, *rp;
	struct addrinfo      *res = NULL;
	struct in_addr       inaddr;
	struct sockaddr_in   addr;
	int                  len = sizeof(addr);
	char                 ipaddr[INET_ADDRSTRLEN];
	struct sockaddr_in   *sp;
	int                  fd = 0;

	if( !sock )
	{
		return -1;
	}

	socket_term(sock);

	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;

	if( inet_aton(sock->host, &inaddr) )
	{
		hints.ai_flags |= AI_NUMERICHOST;
	}

	snprintf(service, sizeof(service), "%d", sock->port);
	
	if( (rv = getaddrinfo(sock->host, service, &hints, &res)) )
	{
		log_error("getaddrinfo() parser [%s:%s] failed: %s\n", sock->host, service, gai_strerror(rv));
		return -2;
	}
	for (rp = res; rp != NULL; rp = rp->ai_next)
	{
		sp = (struct sockaddr_in *) rp->ai_addr;
		memset( ipaddr, 0, sizeof(ipaddr) );

		if( inet_ntop(AF_INET, &sp->sin_addr, ipaddr, sizeof(ipaddr)) )
		{
			log_info("domain name resolution [%s->%s]\n", sock->host, ipaddr);
		}

		fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
		
		if( fd < 0)
		{
			log_error("socket() create failed: %s\n", strerror(errno));
			rv = -3;
			continue;
		}

		rv = connect(fd, rp->ai_addr, len);
		
		if( 0 == rv )
		{
			sock->sockfd = fd;
			log_info("Connect to server[%s:%d] on fd[%d] successfully!\n", sock->host, sock->port, fd);
			break;
		}
		else
		{
			close(fd);
			continue;
		}
	}
	freeaddrinfo(res);
	return rv;
}


//检测连接的状态
int socket_check(socket_cli *sock)
{
	struct tcp_info     info;
	socklen_t           len = sizeof(info);

	if(getsockopt(sock->sockfd, IPPROTO_TCP, TCP_INFO, &info, &len) != 0)    /*1.套接字文件描述符 2.选项协议层 3.具体选项 4.指向缓冲1区的指针，存储获取的选项值 5.输入时为缓冲区大小，输出时为实际获取的选项值长度*/
	{
		return -2;
	}

	if(info.tcpi_state == TCP_ESTABLISHED)    /*结构体info里有个tcpi_state的成员，存储TCP状态，TCP_ESTABLISHED表示连接已建立，返回0，否则返回-1 */
	{
		return 0;
	}
	else
	{
		return -1;
	}
}


